function PageCustomer(user, sd, options) {
  this._user = user;
  this._sd = sd;
  this._options = (options) ? options : null;
  
  this._gui = { bride: { name: document.getElementById('input1'),
                         addrLn1: document.getElementById('input3'),
                         addrLn2: document.getElementById('input5'),
                         phone: document.getElementById('input7'),
                         mobile: document.getElementById('input9'),
                         email: document.getElementById('input11')
                       },
                groom: { name: document.getElementById('input2'),
                         addrLn1: document.getElementById('input3'),
                         addrLn2: document.getElementById('input6'),
                         phone: document.getElementById('input8'),
                         mobile: document.getElementById('input10'),
                         email: document.getElementById('input12')
                       },
                source: document.getElementById('input13'),
                price: { total: document.getElementById('input14'),
                         received: document.getElementById('input15'),
                         anticipated: document.getElementById('input16')
                       },
                orderTab: document.getElementById('td1'),
                orderNew: document.getElementById('button1'),
                order: { type: document.getElementById('input17'),
                         total: document.getElementById('td3'),
                         discount: document.getElementById('td4'),
                         tasks: document.getElementById('td2'),
                         photo: document.getElementById('input18'),
                         video: document.getElementById('input19'),
                         purchases: document.getElementById('table1'),
                         sample: { add: document.getElementById('button4')
                                 },
                         note: document.getElementById('input20')
                       },
                note: document.getElementById('input21'),
                button: document.getElementById('button5')
              };
  
  this._popupBox = new PopupBox();
  
  this._photo = null;
  this._video = null;
  this._purchasePrice = 0;
  
  this._createElements();
};

PageCustomer.prototype._createElements = function() {
  this._loadData();
};

PageCustomer.prototype._loadData = function() {
  this._customer = null;
  this._samples = null;
  this._packages = null;
  this._sources = null;
  this._retrieveCustomer();
  this._retrieveSamples();
  this._retrievePackages();
  this._retrieveSources();
};

PageCustomer.prototype._verifyData = function() {
  if (this._customer && this._samples && this._packages && this._sources) {
    this._updateElements();
  }
};

PageCustomer.prototype._retrieveCustomer = function() {
  var _self = this;
  if (this._options.cid) {
    new RequestUtils()._read('customer', null, 'd.oid = ' + this._options.cid, null, null, null, null, function(result, params) { _self._customer = (result.data.length == 1) ? result.data[0] : null;
                                                                                                                                  _self._verifyData.call(_self);
                                                                                                                                }, null);
  } else {
    this._customer = Customer.instance();
    this._customer.createdDate = this._sd;
    this._customer.sales = this._user;
    this._verifyData();
  }
};

PageCustomer.prototype._retrieveSamples = function() {
  var _self = this;
  new RequestUtils()._read('sample', null, '', null, null, null, null, function(result, params) { _self._samples = result.data;
                                                                                                  _self._verifyData.call(_self);
                                                                                                }, null);
};

PageCustomer.prototype._retrievePackages = function() {
  var _self = this;
  new RequestUtils()._read('package', null, null, null, null, null, null, function(result, params) { _self._packages = result.data;
                                                                                                     _self._verifyData.call(_self);
                                                                                                   }, null);
};

PageCustomer.prototype._retrieveSources = function() {
  var _self = this;
  new RequestUtils()._read('source', null, null, null, null, null, null, function(result, params) { _self._sources = result.data;
                                                                                                    _self._verifyData.call(_self);
                                                                                                  }, null);
};

PageCustomer.prototype._updateElements = function() {
  var _self = this;
  
  if (this._customer.orders.length == 0) {
    var order = MyOrder.instance();
    order.createdDate = this._sd;
    order.music.model = '{{$smarty.const.MODEL_NAME_MUSIC|escape:javascript}}';
    order.precon.model = '{{$smarty.const.MODEL_NAME_PRECON|escape:javascript}}';
    this._customer.orders[this._customer.orders.length] = order;
  }
  
  this._gui.bride.name.value = this._customer.bride.name;
  this._gui.bride.addrLn1.value = this._customer.bride.addrLn1;
  this._gui.bride.addrLn2.value = this._customer.bride.addrLn2;
  this._gui.bride.phone.value = this._customer.bride.phone;
  this._gui.bride.mobile.value = this._customer.bride.mobile;
  this._gui.bride.email.value = this._customer.bride.email;
  
  this._gui.groom.name.value = this._customer.groom.name;
  this._gui.groom.addrLn1.value = this._customer.groom.addrLn1;
  this._gui.groom.addrLn2.value = this._customer.groom.addrLn2;
  this._gui.groom.phone.value = this._customer.groom.phone;
  this._gui.groom.mobile.value = this._customer.groom.mobile;
  this._gui.groom.email.value = this._customer.groom.email;
  
  this._gui.bride.name.onchange = function() { _self._customer.bride.name = this.value; };
  this._gui.bride.addrLn1.onchange = function() { _self._customer.bride.addrLn1 = this.value; };
  this._gui.bride.addrLn2.onchange = function() { _self._customer.bride.addrLn2 = this.value; };
  this._gui.bride.phone.onchange = function() { _self._customer.bride.phone = this.value; };
  this._gui.bride.mobile.onchange = function() { _self._customer.bride.mobile = this.value; };
  this._gui.bride.email.onchange = function() { _self._customer.bride.email = this.value; };
  this._gui.groom.name.onchange = function() { _self._customer.groom.name = this.value; };
  this._gui.groom.addrLn1.onchange = function() { _self._customer.groom.addrLn1 = this.value; };
  this._gui.groom.addrLn2.onchange = function() { _self._customer.groom.addrLn2 = this.value; };
  this._gui.groom.phone.onchange = function() { _self._customer.groom.phone = this.value; };
  this._gui.groom.mobile.onchange = function() { _self._customer.groom.mobile = this.value; };
  this._gui.groom.email.onchange = function() { _self._customer.groom.email = this.value; };
  
  for (var i = 0, il = this._sources.length; i < il; i++) {
    var source = this._sources[i];
    var option = new Option(source.name);
    this._gui.source.options[this._gui.source.options.length] = option;
    if (option.text == this._customer.source){
      this._gui.source.selectedIndex = this._gui.source.options.length - 1;
    }
  }
  if (this._sources.length > 0) {
    this._customer.source = this._gui.source.options[this._gui.source.selectedIndex].text;
    this._gui.source.onchange = function() { _self._customer.source = this.options[this.selectedIndex].text; };
  }
  
  this._gui.note.value = this._customer.note;
  this._gui.note.onchange = function() { _self._customer.note = this.value; };
  
  this._gui.orderNew.onclick = function() { var order = MyOrder.instance();
                                            order.createdDate = _self._sd;
                                            order.music.model = '{{$smarty.const.MODEL_NAME_MUSIC|escape:javascript}}';
                                            order.precon.model = '{{$smarty.const.MODEL_NAME_PRECON|escape:javascript}}';
                                            _self._customer.orders[_self._customer.orders.length] = order;
                                            _self._updateOrders.call(_self, _self._customer.orders.length - 1);
                                          };
  
  this._gui.button.onclick = function() { new RequestUtils()._write('customer', [_self._customer], [], function() { }, null); };
  
  this._updateOrders(0);
};

PageCustomer.prototype._updateOrders = function(index) {
  var unique, td, _self = this;

  var total = 0;
  var received = 0;

  unique = MiscUtils.unique();
  new GTabOrders(unique, this._gui.orderTab, this._customer.orders, index);
  for (var i = 0, il = this._customer.orders.length; i < il; i++) {
    var order = this._customer.orders[i];
    
    total += parseFloat(order.price, 10);
    
    var td = document.getElementById(unique + '_' + i);
    td._index = i;
    td.onclick = function() { _self._updateOrders.call(_self, this._index); };
  }
  this._updateOrder(index);
  
  this._gui.price.total.value = total;
  this._gui.price.received.value = received;
  this._gui.price.anticipated.value = total - received;
};

PageCustomer.prototype._updateOrder = function(index) {
  var unique, table, tr, td, order = this._customer.orders[index], _self = this;
  
  this._photo = (order.photo) ? order.photo : null;
  this._video = (order.video) ? order.video : null;
  
  this._gui.order.type.options.length = 0;
  this._gui.order.photo.options.length = 0;
  this._gui.order.video.options.length = 0;
  DOMUtils.removeTableRows(this._gui.order.purchases, 1);
  DOMUtils.removeChildElements(this._gui.order.tasks);
  
  this._initialiseOrderTask(index);
  this._initialiseStatus(index);

  this._gui.order.total.value = order.price;
  
  this._gui.order.type.options[this._gui.order.type.options.length] = new Option('');
  for (var i = 0, il = OrderType.array().length; i < il; i++) {
    var orderType = OrderType.array()[i];
    option = new Option(orderType);
    this._gui.order.type.options[this._gui.order.type.options.length] = option;
    if (option.text == order.type) {
      this._gui.order.type.selectedIndex = this._gui.order.type.options.length - 1;
    }
  }
  this._gui.order.type.onchange = function() { order.createdDate = _self._sd;
                                               order.type = this.options[this.selectedIndex].text;
                                               order.price = 0;
                                               order.photo = OrderPackage.instance();
                                               order.video = OrderPackage.instance();
                                               order.purchases = [];
                                               order.produces = [];
                                               order.tasks = [];
                                               order.who = null;
                                               order.when = 0
                                               order.music.state = 0;
                                               if (this.options[this.selectedIndex].text == '{{$smarty.const.ORDER_TYPE_WEDDING|escape:javascript}}') {
                                                 order.precon.state = 1;
                                               } else {
                                                 order.precon.state = 0;
                                               }
                                               _self._photo = null;
                                               _self._video = null;
                                               _self._updateOrders.call(_self, index);
                                             };
  
  this._gui.order.photo.options[this._gui.order.photo.options.length] = new Option('');
  for (var i = 0, il = this._packages.length; i < il; i++) {
    var package = this._packages[i];
    if (package.type != '{{$smarty.const.PACKAGE_TYPE_PHOTO|escape:javascript}}' || package.orderType != order.type) {
      continue;
    }
    option = new Option(package.name);
    option._package = package;
    this._gui.order.photo.options[this._gui.order.photo.options.length] = option;
    if (package.id == order.photo.packageId) {
      this._gui.order.photo.selectedIndex = this._gui.order.photo.options.length - 1;
    }
  }
  this._gui.order.photo.onchange = function() { order.price -= (_self._photo) ? parseFloat(_self._photo.price, 10) : 0;
                                                order.price += (this.options[this.selectedIndex]._package) ? parseFloat(this.options[this.selectedIndex]._package.price, 10) : 0;
                                                order.photo = OrderPackage.instance();
                                                order.photo.packageId = (this.options[this.selectedIndex]._package) ? this.options[this.selectedIndex]._package.id : 0;
                                                order.photo.name = (this.options[this.selectedIndex]._package) ? this.options[this.selectedIndex]._package.name : '';
                                                order.photo.price = (this.options[this.selectedIndex]._package) ? this.options[this.selectedIndex]._package.price : 0;
                                                order.photo.hours = (this.options[this.selectedIndex]._package) ? this.options[this.selectedIndex]._package.hours : 0;
                                                order.photo.persons = (this.options[this.selectedIndex]._package) ? this.options[this.selectedIndex]._package.persons : 0;
                                                order.photo.makeup = (this.options[this.selectedIndex]._package) ? this.options[this.selectedIndex]._package.makeup : '';
                                                _self._photo = order.photo;
                                                _self._addPackagePurchases.call(_self, '{{$smarty.const.PACKAGE_TYPE_PHOTO|escape:javascript}}', index, this.options[this.selectedIndex]._package);
                                                _self._updateOrders.call(_self, index);
                                              };
  
  this._gui.order.video.options[this._gui.order.video.options.length] = new Option('');
  for (var i = 0, il = this._packages.length; i < il; i++) {
    var package = this._packages[i];
    if (package.type != '{{$smarty.const.PACKAGE_TYPE_VIDEO|escape:javascript}}' || package.orderType != order.type) {
      continue;
    }
    option = new Option(package.name);
    option._package = package;
    this._gui.order.video.options[this._gui.order.video.options.length] = option;
    if (package.id == order.video.packageId) {
      this._gui.order.video.selectedIndex = this._gui.order.video.options.length - 1;
    }
  }
  this._gui.order.video.onchange = function() { order.price -= (_self._video) ? parseFloat(_self._video.price, 10) : 0;
                                                order.price += (this.options[this.selectedIndex]._package) ? parseFloat(this.options[this.selectedIndex]._package.price, 10) : 0;
                                                order.video = OrderPackage.instance();
                                                order.video.packageId = (this.options[this.selectedIndex]._package) ? this.options[this.selectedIndex]._package.id : 0;
                                                order.video.name = (this.options[this.selectedIndex]._package) ? this.options[this.selectedIndex]._package.name : '';
                                                order.video.price = (this.options[this.selectedIndex]._package) ? this.options[this.selectedIndex]._package.price : 0;
                                                order.video.hours = (this.options[this.selectedIndex]._package) ? this.options[this.selectedIndex]._package.hours : 0;
                                                order.video.persons = (this.options[this.selectedIndex]._package) ? this.options[this.selectedIndex]._package.persons : 0;
                                                order.video.makeup = (this.options[this.selectedIndex]._package) ? this.options[this.selectedIndex]._package.makeup : '';
                                                _self._video = order.video;
                                                if (this.options[this.selectedIndex]._package) {
                                                  order.music.state = 1;
                                                }
                                                _self._addPackagePurchases.call(_self, '{{$smarty.const.PACKAGE_TYPE_VIDEO|escape:javascript}}', index, this.options[this.selectedIndex]._package);
                                                _self._updateOrders.call(_self, index);
                                              };
  
  tr = this._gui.order.purchases.insertRow(-1);
  td = tr.insertCell(-1);
  td.style.height = '10px'; 
  
  unique = MiscUtils.unique();
  for (var i = 0, il = order.purchases.length; i < il; i++) {
    var purchase = order.purchases[i];
    
    tr = this._gui.order.purchases.insertRow(-1);
    new GtrPurchase(unique, tr, purchase);
  }
  
  this._gui.order.sample.add.onclick = function() { var purchase = OrderPurchase.instance();
                                                    purchase.product = Product.instance();
                                                    purchase.product.sampleId = _self._samples[0].id;
                                                    purchase.product.name = _self._samples[0].name;
                                                    purchase.product.price = _self._samples[0].price;
                                                    purchase.product.type = _self._samples[0].type;
                                                    purchase.product.model = _self._samples[0].model;
                                                    purchase.product.designId = MiscUtils.unique();
                                                    purchase.product.selects = [];
                                                    for (var i = 0, il = _self._samples[0].attributes.length; i < il; i++) {
                                                      var attribute = _self._samples[0].attributes[i];
                                                      var select = ProductSelect.instance();
                                                      select.sampleAttributeOptionId = attribute.options[0].id;
                                                      select.sampleAttributeId = attribute.id;
                                                      select.sampleAttributeName = attribute.name;
                                                      select.name = attribute.options[0].name;
                                                      purchase.product.selects[purchase.product.selects.length] = select;
                                                    }
                                                    purchase.quantity = 1;
                                                    purchase.price = purchase.product.price;
                                                    order.purchases[order.purchases.length] = purchase;
                                                    order.price += parseFloat(purchase.price, 10);
                                                    _self._purchasePrice = purchase.price;
                                                    _self._addAdditionalPurchase.call(_self, index, purchase);
                                                  };
  
  this._updateOrderTask(index);
  
  this._gui.order.note.value = order.note;
  this._gui.order.note.onchange = function() { order.note = this.value; };
};

PageCustomer.prototype._updateOrderTask = function(index) {
  var table, tr, td, order = this._customer.orders[index], _self = this;
  
  DOMUtils.removeChildElements(this._gui.order.tasks);
  
  table = document.createElement('table');
  this._gui.order.tasks.appendChild(table);
  
  var pTotal = 0;
  var vTotal = 0;
  for (var i = 0, il = order.tasks.length; i < il; i++) {
    var task = order.tasks[i];
    for (var j = 0, jl = task.photographers.length; j < jl; j++) {
      var photographer = task.photographers[j];
      pTotal += parseFloat(photographer.estimates, 10);
      pTotal += parseFloat(photographer.extras, 10);
    }
    for (var j = 0, jl = task.videographers.length; j < jl; j++) {
      var videographer = task.videographers[j];
      vTotal += parseFloat(videographer.estimates, 10);
      vTotal += parseFloat(videographer.extras, 10);
    }
  }
  
  tr = table.insertRow(-1);
  td = tr.insertCell(-1);
  td.appendChild(document.createTextNode(order.photo.hours * order.photo.persons + '(' + pTotal + ') ' + order.video.hours * order.video.persons + '(' + vTotal + ')'));
  td = tr.insertCell(-1);
  var input = document.createElement('img');
  input.src = 'images/pageasst/pagecustomer/addtask.png';
  input.style.cursor = 'pointer';
  input.onclick = function() { order.tasks[order.tasks.length] = OrderTask.instance();
                               if (order.type == '{{$smarty.const.ORDER_TYPE_WEDDING|escape:javascript}}') {
                                 order.precon.date = DlmanageUtils.earliest(order);
                               }
                               _self._updateOrderTask.call(_self, index);
                             };
  td.appendChild(input);
  
  table = document.createElement('table');
  this._gui.order.tasks.appendChild(table);
  
  for (var i = 0, il = order.tasks.length; i < il; i++) {
    var task = order.tasks[i];
    
    tr = table.insertRow(-1);
    td = tr.insertCell(-1);
    td.style.textAlign = 'left';
    new EditOrderTask(td, task, function() { _self._updateOrderTask.call(_self, index); }, { parent: order, index: i });
  }
};

PageCustomer.prototype._addPackagePurchases = function(packageType, index, package) {
  var order = this._customer.orders[index], i = 0;
  while (i < order.purchases.length) {
    var purchase = order.purchases[i];
    if (packageType == purchase.packageType) {
      order.purchases.splice(i, 1);
      i = 0;
    } else {
      i++;
    }
  }
  if (package) {
    var addon = MiscUtils.unique(); 
    for (var i = 0, il = package.purchases.length; i < il; i++) {
      var purchase = MiscUtils.clone(package.purchases[i]);
      purchase.trackId = MiscUtils.unique();
      purchase.designId = parseFloat(purchase.designId, 10) + parseFloat(addon, 10);
      purchase.packageType = package.type;
      delete purchase.id;
      delete purchase.product.id;
      for (var j = 0, jl = purchase.product.selects.length; j < jl; j++) {
        var select = purchase.product.selects[j];
        delete select.id;
      }
      order.purchases[order.purchases.length] = purchase;
    }
  }
};

PageCustomer.prototype._addAdditionalPurchase = function(index, purchase) {
  var div, order = this._customer.orders[index], _self = this;

  order.price -= _self._purchasePrice;
  order.price += parseFloat(purchase.price, 10);
  _self._purchasePrice = purchase.price;
  
  var func1 = function() { _self._popupBox._close();
                           _self._updateOrders.call(_self, index);
                         };
  var func2 = function() { _self._popupBox._close();
                           order.price -= purchase.price;
                           order.purchases.splice(order.purchases.length - 1, 1);
                           _self._updateOrders.call(_self, index);
                         };
  var div = document.createElement('div');
  MiscUtils.dialog(this._popupBox, null, div, func1, func2);
  new EditPurchase(div, purchase, function() { _self._addAdditionalPurchase.call(_self, index, purchase); }, { parent: order, samples: _self._samples });
};

PageCustomer.prototype._initialiseOrderTask = function(index) {
  var task, makeup, order = this._customer.orders[index];
  
  var pCount = 0;
  var vCount = 0;
  var mCount = 0;
  for (var i = 0, il = order.tasks.length; i < il; i++) {
    var task = order.tasks[i];
    pCount += parseFloat(task.photographers.length, 10);
    vCount += parseFloat(task.videographers.length, 10);
    mCount += parseFloat(task.makeups.length, 10);
  }
  
  if (order.photo.packageId) {
    if (pCount == 0) {
      if (!order.tasks || order.tasks.length == 0) {
        order.tasks = [OrderTask.instance()];
      }
      task = order.tasks[0];
      for (var i = 0, il = order.photo.persons; i < il; i++) {
        var photographer = OrderTaskUser.instance();
        photographer.status.date = task.date;
        photographer.status.state = 1;
        photographer.status.model = '{{$smarty.const.MODEL_NAME_USER_PHOTOGRAPHER|escape:javascript}}';
        photographer.estimates = order.photo.hours;
        photographer.type = 'photographer';
        task.photographers[task.photographers.length] = photographer;
      }
    }
  } else {
    for (var i = 0, il = order.tasks.length; i < il; i++) {
      var task = order.tasks[i];
      task.photographers = [];
    }
  }

  if (order.video.packageId) {
    if (vCount == 0) {
      if (!order.tasks || order.tasks.length == 0) {
        order.tasks = [OrderTask.instance()];
      }
      task = order.tasks[0];
      for (var i = 0, il = order.video.persons; i < il; i++) {
        var videographer = OrderTaskUser.instance();
        videographer.status.date = task.date;
        videographer.status.state = 1;
        videographer.status.model = '{{$smarty.const.MODEL_NAME_USER_VIDEOGRAPHER|escape:javascript}}';
        videographer.estimates = order.video.hours;
        videographer.type = 'videographer';
        task.videographers[task.videographers.length] = videographer;
      }
    }
  } else {
    for (var i = 0, il = order.tasks.length; i < il; i++) {
      var task = order.tasks[i];
      task.videographers = [];
    }
  }
  
  if (mCount == 0) {
    if (order.photo && order.photo.makeup != '') {
      if (!order.tasks || order.tasks.length == 0) {
        order.tasks = [OrderTask.instance()];
      }
      task = order.tasks[0];
      var makeup = OrderTaskUser.instance();
      makeup.status.date = task.date;
      makeup.status.state = 1;
      makeup.status.model = '{{$smarty.const.MODEL_NAME_USER_MAKEUP|escape:javascript}}';
      makeup.type = order.photo.makeup;
      task.makeups[task.makeups.length] = makeup;
    }
    if (order.video && order.video.makeup != '') {
      if (!order.tasks || order.tasks.length == 0) {
        order.tasks = [OrderTask.instance()];
      }
      task = order.tasks[0];
      var makeup = OrderTaskUser.instance();
      makeup.status.date = task.date;
      makeup.status.state = 1;
      makeup.status.model = '{{$smarty.const.MODEL_NAME_USER_MAKEUP|escape:javascript}}';
      makeup.type = order.video.makeup;
      task.makeups[task.makeups.length] = makeup;
    }
  }
  
  if (!order.photo.packageId && !order.photo.packageId) {
    order.tasks = [];
  }
};

PageCustomer.prototype._initialiseStatus = function(index) {
  var order = this._customer.orders[index];
  
  var date = DlmanageUtils.earliest(order);
  
  order.music.date = date;
  order.precon.date = date;
  
  for (var i = 0, il = order.tasks.length; i < il; i++) {
    var task = order.tasks[i];
    
    for (var j = 0, jl = task.photographers.length; j < jl; j++) {
      var photographer = task.photographers[j];
      photographer.status.date = date;
    }
    for (var j = 0, jl = task.videographers.length; j < jl; j++) {
      var videographer = task.videographers[j];
      videographer.status.date = date;
    }
    for (var j = 0, jl = task.makeups.length; j < jl; j++) {
      var makeup = task.makeups[j];
      makeup.status.date = date;
    }
  }
};
